package com.gitee.dufafei.plugin.variable;

import com.gitee.dufafei.plugin.consts.Const;
import org.apache.commons.lang3.StringUtils;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class Variable implements VariableSpace {

    private Map<String, String> properties;
    private VariableSpace parent;
    private Map<String, String> injection;
    private boolean initialized;

    public Variable() {
        properties = new ConcurrentHashMap<>();
        parent = null;
        injection = null;
        initialized = false;
    }

    @Override
    public void initializeVariablesFrom(VariableSpace parent) {
        this.parent = parent;
        // Clone the system properties to avoid ConcurrentModificationException while iterating
        // and then add all of them to properties variable.
        Set<String> systemPropertiesNames = System.getProperties().stringPropertyNames();
        for (String key : systemPropertiesNames) {
            properties.put(key, System.getProperties().getProperty(key));
        }
        if (parent != null) {
            copyVariablesFrom(parent);
        }
        if (injection != null) {
            properties.putAll(injection);
            injection = null;
        }
        initialized = true;
    }

    @Override
    public void copyVariablesFrom(VariableSpace space) {
        if (space != null && this != space) {
            String[] variableNames = space.listVariables();
            for (String variableName : variableNames) {
                properties.put(variableName, space.getVariable(variableName));
            }
        }
    }

    @Override
    public void injectVariables(Map<String, String> prop) {
        if (initialized) {
            // variables are already initialized
            if (prop != null) {
                for (String key : prop.keySet()) {
                    String value = prop.get(key);
                    if (StringUtils.isNotEmpty(key)) {
                        properties.put(key, Const.NVL(value, ""));
                    }
                }
                injection = null;
            }
        } else {
            // We have our own personal copy, so changes afterwards
            // to the input properties don't affect us.
            injection = new Hashtable<>();
            for (String key : prop.keySet()) {
                String value = prop.get(key);
                if (StringUtils.isNotEmpty(key)) {
                    injection.put(key, Const.NVL(value, ""));
                }
            }
        }
    }

    @Override
    public void shareVariablesWith(VariableSpace space) {
        // not implemented in here...
        // done by pointing to the same VariableSpace
    }

    @Override
    public VariableSpace getParentVariableSpace() { return parent; }

    @Override
    public void setParentVariableSpace(VariableSpace parent) { this.parent = parent; }

    @Override
    public String getVariable(String variableName, String defaultValue) {
        String var = properties.get(variableName);
        if (var == null) {
            return defaultValue;
        }
        return var;
    }

    @Override
    public String getVariable(String variableName) { return properties.get(variableName); }

    @Override
    public void setVariable(String variableName, String variableValue) {
        if (variableValue != null) {
            properties.put(variableName, variableValue);
        } else {
            properties.remove(variableName);
        }
    }

    @Override
    public boolean getBooleanValue(String variableName, boolean defaultValue) {
        if (StringUtils.isNotEmpty(variableName)) {
            String value = environmentSubstitute(variableName);
            if (StringUtils.isEmpty(value)) {
                return "Y".equalsIgnoreCase(value)
                        || "YES".equalsIgnoreCase(value)
                        || "TRUE".equalsIgnoreCase(value)
                        || "1".equals(value);
            }
        }
        return defaultValue;
    }

    @Override
    public String environmentSubstitute(String aString) {
        if (aString == null || aString.length() == 0) {
            return aString;
        }
        return VariableUtils.environmentSubstitute(aString, properties);
    }

    @Override
    public String[] listVariables() {
        Set<String> keySet = properties.keySet();
        return keySet.toArray(new String[0]);
    }
}
